#include <stdio.h>
#include <ulog/ulog.h>
#include "iic_function.h"
#include "camera_config.h"
#include "drv_isp.h"
#include "soc.h"
#include "pin_name.h"
#include "ov2732_drv.h"
#include "drv_gpio.h"
//#include "image_proc.h"
#include "reg_deal.h"
//#include "test_hist.h"
#include "ov2732_drv.h"
#include "clib_define.h"

#define OV2732_MODULE "ov2732"

static i2c_dev_t i2c_ov2732;
static i2c_dev_t i2c_ov2732_rgb;
uint32_t g_ov2732_configed = 0;
extern gpio_pin_handle_t gpio_pin_handle;
ov2732_config_t g_ov2732_config = OV2732_NO_NEED_CONFIG;
typedef enum 
{
    CFG_MODE_NONE = 0,		
    CFG_MODE_RGB_1080P_YUV,
    CFG_MODE_RGB_720P_YUV,
    CFG_MODE_RGB_640X360_RGB,
    CFG_MODE_IR_720P_YUV,
    CFG_MODE_IR_720P_RAW10,
    CFG_MODE_IR_640x360_RAW,    
	CFG_MODE_IR_1080P_RAW10,
}SENSOR_CFG_MODE_E;


#define AOGPIO_BASE						0xFFF02000
#define GPIO_SWPORTA_DR					0x0
#define GPIO_SWPORTA_DDR				0x4

static void set_aogpio_high(uint8_t aogpio)
{
	uint32_t data;
	data = rd(AOGPIO_BASE + GPIO_SWPORTA_DDR);
	wr(AOGPIO_BASE + GPIO_SWPORTA_DDR, data | (1 << aogpio));
	data = rd(AOGPIO_BASE + GPIO_SWPORTA_DR);
	wr(AOGPIO_BASE + GPIO_SWPORTA_DR, data | (1 << aogpio));
}

static void set_aogpio_low(uint8_t aogpio)
{
	uint32_t data;
	data = rd(AOGPIO_BASE + GPIO_SWPORTA_DDR);
	wr(AOGPIO_BASE + GPIO_SWPORTA_DDR, data | (1 << aogpio));
	data = rd(AOGPIO_BASE + GPIO_SWPORTA_DR);
	wr(AOGPIO_BASE + GPIO_SWPORTA_DR, data & (~(1 << aogpio)));
}


regval_list ov2732_1920x1080_raw10[] = {
	{0x0103,0x01},		/* soft reset */
	{0x0305,0x3c},
	{0x0307,0x00},
	{0x0308,0x03},
	{0x0309,0x03},
	{0x0327,0x07},
	{0x3016,0x32},
	{0x3000,0x00},
	{0x3001,0x00},
	{0x3002,0x00},
	{0x3013,0x00},
	{0x301f,0xf0},
	{0x3023,0xf0},
	{0x3020,0x9b},
	{0x3022,0x51},
	{0x3106,0x11},
	{0x3107,0x01},
	{0x3500,0x00},
	{0x3501,0x40},
	{0x3502,0x00},
	{0x3503,0x88},
	{0x3505,0x83},
	{0x3508,0x01},
	{0x3509,0x80},
	{0x350a,0x04},
	{0x350b,0x00},
	{0x350c,0x00},
	{0x350d,0x80},
	{0x350e,0x04},
	{0x350f,0x00},
	{0x3510,0x00},
	{0x3511,0x00},
	{0x3512,0x20},
	{0x3600,0x55},
	{0x3601,0x52},
	{0x3612,0xb5},
	{0x3613,0xb3},
	{0x3616,0x83},
	{0x3621,0x00},
	{0x3624,0x06},
	{0x3642,0x88},
	{0x3660,0x00},
	{0x3661,0x00},
	{0x366a,0x64},
	{0x366c,0x00},
	{0x366e,0xff},
	{0x366f,0xff},
	{0x3677,0x11},
	{0x3678,0x11},
	{0x3679,0x0c},
	{0x3680,0xff},
	{0x3681,0x16},
	{0x3682,0x16},
	{0x3683,0x90},
	{0x3684,0x90},
	{0x3768,0x04},
	{0x3769,0x20},
	{0x376a,0x04},
	{0x376b,0x20},
	{0x3620,0x80},
	{0x3662,0x10},
	{0x3663,0x24},
	{0x3665,0xa0},
	{0x3667,0xa6},
	{0x3674,0x01},
	{0x373d,0x24},
	{0x3741,0x28},
	{0x3743,0x28},
	{0x3745,0x28},
	{0x3747,0x28},
	{0x3748,0x00},
	{0x3749,0x78},
	{0x374a,0x00},
	{0x374b,0x78},
	{0x374c,0x00},
	{0x374d,0x78},
	{0x374e,0x00},
	{0x374f,0x78},
	{0x3766,0x12},
	{0x37e0,0x00},
	{0x37e6,0x04},
	{0x37e5,0x04},
	{0x37e1,0x04},
	{0x3737,0x04},
	{0x37d0,0x0a},
	{0x37d8,0x04},
	{0x37e2,0x08},
	{0x3739,0x10},
	{0x37e4,0x18},
	{0x37e3,0x04},
	{0x37d9,0x10},
	{0x4040,0x04},
	{0x4041,0x0f},
	{0x4008,0x00},
	{0x4009,0x0d},
	{0x37a1,0x14},
	{0x37a8,0x16},
	{0x37ab,0x10},
	{0x37c2,0x04},
	{0x3705,0x00},
	{0x3706,0x28},
	{0x370a,0x00},
	{0x370b,0x78},
	{0x3714,0x24},
	{0x371a,0x1e},
	{0x372a,0x03},
	{0x3756,0x00},
	{0x3757,0x0e},
	{0x377b,0x00},
	{0x377c,0x0c},
	{0x377d,0x20},
	{0x3790,0x28},
	{0x3791,0x78},
	{0x3800,0x00},
	{0x3801,0x00},
	{0x3802,0x00},
	{0x3803,0x04},
	{0x3804,0x07},
	{0x3805,0x8f},
	{0x3806,0x04},
	{0x3807,0x43},
	{0x3808,0x07},
	{0x3809,0x80},
	{0x380a,0x04},
	{0x380b,0x38},
	{0x380c,0x02},	//0x0278=632	0x0790=1936
	{0x380d,0x78},
	{0x380e,0x04},	//0x04a0=1184	0x0478=1144
	{0x380f,0xa0},
	{0x3811,0x08},
	{0x3813,0x04},
	{0x3814,0x01},
	{0x3815,0x01},
	{0x3816,0x01},
	{0x3817,0x01},
	{0x381d,0x40},
	{0x381e,0x02},
	{0x3820,0x88},
	{0x3821,0x00},
	{0x3822,0x04},
	{0x3835,0x00},
	{0x4303,0x19},
	{0x4304,0x19},
	{0x4305,0x03},
	{0x4306,0x81},
	{0x4503,0x00},
	{0x4508,0x14},
	{0x450a,0x00},
	{0x450b,0x40},
	{0x4833,0x08},
	{0x5000,0xa9},
	{0x5001,0x09},
	{0x3b00,0x00},
	{0x3b02,0x00},
	{0x3b03,0x00},
	{0x3c80,0x08},
	{0x3c82,0x00},
	{0x3c83,0xb1},
	{0x3c87,0x08},
	{0x3c8c,0x10},
	{0x3c8d,0x00},
	{0x3c90,0x00},
	{0x3c91,0x00},
	{0x3c92,0x00},
	{0x3c93,0x00},
	{0x3c94,0x00},
	{0x3c95,0x00},
	{0x3c96,0x00},
	{0x3c97,0x00},
	{0x3c98,0x00},
	{0x4000,0xf3},
	{0x4001,0x60},
	{0x4002,0x00},
	{0x4003,0x40},
	{0x4090,0x14},
	{0x4601,0x10},
	{0x4701,0x00},
	{0x4708,0x09},
	{0x470a,0x00},
	{0x470b,0x40},
	{0x470c,0x81},
	{0x480c,0x12},
	{0x4710,0x06},
	{0x4711,0x00},
	{0x4837,0x12},
	{0x4800,0x00},
	{0x4c01,0x00},
	{0x5036,0x00},
	{0x5037,0x00},
	{0x580b,0x0f},
	{0x4903,0x80},
	{0x484b,0x05},
	{0x400a,0x00},
	{0x400b,0x90},
	{0x4003,0x40},
	{0x5000,0xf9},
	{0x5200,0x1b},
	{0x4837,0x16},
	{0x380e,0x04},	//04a0
	{0x380f,0x78},
	{0x3500,0x00},
	{0x3501,0x49},
	{0x3502,0x80},
	{0x3508,0x02},
	{0x3509,0x80},
	{0x3d8c,0x11},
	{0x3d8d,0xf0},
	{0x5180,0x00},
	{0x5181,0x10},
	{0x36a0,0x16},
	{0x36a1,0x50},
	{0x36a2,0x60},
	{0x36a3,0x80},
	{0x36a4,0x00},
	{0x36a5,0xa0},
	{0x36a6,0x00},
	{0x36a7,0x50},
	{0x36a8,0x00},
	{0x36a9,0x50},
	{0x36aa,0x00},
	{0x36ab,0x50},
	{0x36ac,0x00},
	{0x36ad,0x50},
	{0x36ae,0x00},
	{0x36af,0x50},
	{0x36b0,0x00},
	{0x36b1,0x50},
	{0x36b2,0x00},
	{0x36b3,0x50},
	{0x36b4,0x00},
	{0x36b5,0x50},
	{0x36b9,0xee},
	{0x36ba,0xee},
	{0x36bb,0xee},
	{0x36bc,0xee},
	{0x36bd,0x0e},
	{0x36b6,0x08},
	{0x36b7,0x08},
	{0x36b8,0x10},
	{0x0100 , 0x01},
//	{0x5080,0x84},//color bar, for test

};

regval_list ov2732_strobe_init[] = {
	//@@ Strobe Control init example �?ov2732
{0x3006, 0x0c}, //;08 ; enable 
{0x3210, 0x04}, //;10 ; disable GPIO trigger mode
{0x3007, 0x02}, //
{0x301c, 0xf2}, //;22 ;enable the clock for group write, [1]rst_grp 
{0x3020, 0x20}, //;20 ; strobe logic always on 
{0x3025, 0x02}, //
{0x382c, 0x09},
{0x382d, 0x9a},
{0x3920, 0xff}, //
{0x3923, 0x00}, //; delay 
{0x3924, 0x00}, //; 
{0x3925, 0x00}, //; width 
{0x3926, 0x00}, //
{0x3927, 0x00}, //
{0x3928, 0x80}, //
{0x392b, 0x00}, //
{0x392c, 0x00}, //
{0x392d, 0x05}, //;02 ;=HTS
{0x392e, 0x8e}, //;d8 
{0x392f, 0xcb}, //
{0x38b3, 0x07}, //
{0x3885, 0x07}, //
{0x382b, 0x5a}, //
{0x3670, 0x68}, //


	
};

regval_list ov2732_strobe_time[] = {
	//@@Below need program every time during exposure update strobe 10ms
	//;Exp 1, Gain 1x
	//;Exp 1, Gain 1x
	{0x3208,0x00},//;
	// {0x3501,0x07},//;0f; exp
	// {0x3502,0x66},//;10
	{0x3501,0x03},
	{0x3502,0xB6},

	{0x3508,0x00},//; gain
	// {0x3509,0x20},//
	{0x3509,0x10},//
	// {0x3927,0x00},//;00
	// {0x3928,0x76},//; strobe width 10ms
	// {0x3929,0x03},//; strobe start point is VTS �?Texposure �?7 �?Tnegative, 
	// {0x392A,0x5A},// e.g. VTS = 0x3d8, Texposure = 0x58, if Tnegative = 1, then R3929/2a = 0x378
	{0x3927,0x00},//;00
	{0x3928,0x3B},//; strobe width 10ms
	{0x3929,0x03},//; strobe start point is VTS �?Texposure �?7 �?Tnegative, 
	{0x392A,0x95},// e.g. VTS = 0x3d8, Texposure = 0x58, if Tnegative = 1, then R3929/2a = 0x378
	{0x3208,0x10},//;
	{0x3208,0xA0},//

};

const  regval_list ov2732_1280x720_master_read[] = {
	{0x3001,0x00},
};


const  regval_list ov2732_sw_standby[] = {
	{0x0100 , 0x00},
};

const  regval_list ov2732_streaming[] = {
	{0x0100 , 0x01},
};


int ov2732_mode_config(sensor_mode_t mode)
{	
	int ret = 0;
	//printf("%s >>>>  %d, mode %d \n",__func__, __LINE__, mode);

	if (STANDBY == mode) {
		ret = iic_write_array(&i2c_ov2732, (regval_list *)ov2732_sw_standby, ARRAY_SIZE(ov2732_sw_standby));
		if (ret < 0) {
			return -1;
		}
	} else {
		ret = iic_write_array(&i2c_ov2732, (regval_list *)ov2732_streaming, ARRAY_SIZE(ov2732_streaming));
		if (ret < 0) {
			return -1;
		}
	}

	return 0;
}

static int ov2732_check_id(i2c_dev_t *i2c)
{
    int ret = 0;
    uint8_t data = 0;

    //printf("check 2732 id: \n");
    
    ret = iic_read(i2c,  OV2732_REG_CHIPID_H, &data);
    printf("H %02x \n",data);
    if (ret < 0 || data != 0x27)
        return -1;
    ret = iic_read(i2c, OV2732_REG_CHIPID_L, &data);
    printf("L %02x \n",data);
   	if (ret < 0 || data != 0x32)
        return -1;

    return ret;
}

int ov2732_hw_reset(void)
{
#if 0
	//Config USI3_SD0 <=> GPIOB14  
    gpio_pin_handle = csi_gpio_pin_initialize(PB14, 0);
    csi_gpio_pin_config_mode(gpio_pin_handle, GPIO_MODE_PULLDOWN);
    csi_gpio_pin_config_direction(gpio_pin_handle, GPIO_DIRECTION_OUTPUT);
    csi_gpio_pin_write(gpio_pin_handle, 0);
	aos_msleep(500);
    csi_gpio_pin_write(gpio_pin_handle, 1);

#else
		set_aogpio_high(OV2732_IR_RSTN_INDEX);
		aos_msleep(50);
		set_aogpio_low(OV2732_IR_RSTN_INDEX);
		aos_msleep(50);
		set_aogpio_high(OV2732_IR_RSTN_INDEX);


#endif 
	
    return 0;
}

int ov2732_init_strobe(void)
{
	int ret = 0;

	ret = iic_write_array(&i2c_ov2732, (regval_list *)ov2732_strobe_init, ARRAY_SIZE(ov2732_strobe_init));
	if (ret < 0) {
		return -1;
	}

	return 0;
}

int ov2732_config_strobe_time(void)
{
	int ret = 0;

	ret = iic_write_array(&i2c_ov2732, (regval_list *)ov2732_strobe_time, ARRAY_SIZE(ov2732_strobe_time));
	if (ret < 0) {
		return -1;
	}

	return 0;

}

int ov2732_strobe_config(void)
{
	int ret = 0;

	ret = ov2732_init_strobe();
	if (ret < 0) {
		printf("ov2732 init strobe err!\n");
		return -1;
	}

	ret = ov2732_config_strobe_time();
	if (ret < 0) {
		printf("ov2732 config strobe time err!\n");
		return -1;
	}

	return 0;
}

int ov2732_init(i2c_dev_t *i2c, uint32_t dev_addr)
{
   	memcpy(&i2c_ov2732, i2c, sizeof(i2c_dev_t));
    i2c_ov2732.config.dev_addr = dev_addr;
    //Check the id of camera
   	int ret = ov2732_check_id(&i2c_ov2732);
    if (ret < 0) {
        printf("camera ov2732 check id failed !!! \n");
        return ret;
    }
    return ret;
}

int ov2732_init_rgb(i2c_dev_t *i2c, uint32_t dev_addr)
{
   	memcpy(&i2c_ov2732_rgb, i2c, sizeof(i2c_dev_t));
    i2c_ov2732_rgb.config.dev_addr = dev_addr;
    //Check the id of camera
   	int ret = ov2732_check_id(&i2c_ov2732_rgb);
    if (ret < 0) {
        printf("camera ov2732 check id failed !!! \n");
        return ret;
    }
    return ret;
}

int32_t ov2732_config(imageFormat_t cfg)
{
	int32_t ret = 0;

	if( R_1080P == cfg.res && RAW10 == cfg.format)
	{
		ret = iic_write_array(&i2c_ov2732, (regval_list *)ov2732_1920x1080_raw10, ARRAY_SIZE(ov2732_1920x1080_raw10));
		if (ret < 0) {
			return -1;
		}
	}

#if 0	
	else if(R_720P == cfg.res && RAW8 == cfg.format)
	{
		ret = iic_write_array(&i2c_ov2732, (struct regval_list *)ov2732_1280x720_raw8, ARRAY_SIZE(ov2732_1280x720_raw8));
		if (ret < 0) {
			return -1;
		}
    }
#endif 


#if 0 //tmp remove
	ret = ov2732_strobe_config();
	if (ret < 0)
		return -1;
#endif 
	return ret;
}


int ov2732_switch_exposure_para(exposure_scene_t scene)
{
    int ret = 0;
    static regval_list expo_arg[] = { 
        {0x3501,0x4A},	//expo H		
        {0x3502,0x00},  //expo L
        {0x3508,0x02},
        {0x3509,0x80},  //gain
    };

    if (EXPOSURE_PARA_OUTDOOR == scene) {
        // expo 3ms
		expo_arg[0].data = 0x20;
		expo_arg[1].data = 0x00;
		expo_arg[2].data = 0x00;
		expo_arg[3].data = 0x80;

    } else {
        // expo 10ms
        expo_arg[0].data = 0x3e;
        expo_arg[1].data = 0xa0;
        expo_arg[2].data = 0x02;
		expo_arg[3].data = 0x80;
    }

	LOGI(OV2732_MODULE, "cur_scene %s", scene == EXPOSURE_PARA_INDOOR ? "indoor" : "outdoor");
    LOGI(OV2732_MODULE, "expo_h:%02x, expo_l: %02x, gain:%02x\n", expo_arg[0].data, expo_arg[1].data, expo_arg[2].data);
	//printf( "cur_scene %s", scene == EXPOSURE_PARA_INDOOR ? "indoor" : "outdoor");
    //printf( "expo_h:%02x, expo_l: %02x, gain:%02x\n", expo_arg[0].data, expo_arg[1].data, expo_arg[2].data);

    ret = iic_write_array(&i2c_ov2732, (regval_list *)expo_arg, ARRAY_SIZE(expo_arg));
    if(ret != 0) {
        LOGI(OV2732_MODULE, " 2732 expo config error:%d \n", ret);
    }
    return ret; 
}

int ov2732_set_exposure_para(regval_list *expo_arg)
{
    int ret = 0;

    ret = iic_write_array(&i2c_ov2732, (regval_list *)expo_arg, 4);
    if(ret != 0) {
        LOGI(OV2732_MODULE, " 2732 expo config error:%d \n", ret);
    }
	
    return ret; 
}


int ov2732_get_exposure_para(regval_list *expo_arg)
{
    int ret = 0;

    ret = iic_read_array(&i2c_ov2732, (regval_list *)expo_arg, 4);
    if(ret != 0) {
        LOGI(OV2732_MODULE, " 2732 expo config error:%d \n", ret);
    }
	
    return ret; 
}

int ov2732_rgb_get_exposure_para(regval_list *expo_arg)
{
    int ret = 0;

    ret = iic_read_array(&i2c_ov2732_rgb, (regval_list *)expo_arg, 4);
    if(ret != 0) {
        LOGI(OV2732_MODULE, " 2732 expo config error:%d \n", ret);
    }
	
    return ret; 
}



int ov2732_set_spk_exposure_para(exposure_scene_t scene)
{
    int ret = 0;

	return ret;
	
    static regval_list expo_arg[] = { 
        {0x3501,0},	//expo H		
        {0x3502,0},  //expo L
        {0x3509,0},  //gain
    };

    if (EXPOSURE_PARA_OUTDOOR == scene) {
        // expo 2ms
        expo_arg[0].data = 0x03;
        expo_arg[1].data = 0xb6;
        expo_arg[2].data = 0x10;

    } else {
        // expo 2ms
        expo_arg[0].data = 0x03;
        expo_arg[1].data = 0xb6;
        expo_arg[2].data = 0x80;
    }

    LOGI(OV2732_MODULE, "expo_h:%02x, expo_l: %02x, gain:%02x\n", expo_arg[0].data, expo_arg[1].data, expo_arg[2].data);

    ret = iic_write_array(&i2c_ov2732, (regval_list *)expo_arg, ARRAY_SIZE(expo_arg));
    if(ret != 0) {
        LOGE(OV2732_MODULE, " 2732 expo config error:%d \n", ret);
    }
    return ret; 
}

int set_ov2732_reglist(regval_list reglist[], uint32_t reglist_len)
{
    int ret = 0;

    ret = iic_write_array(&i2c_ov2732, (regval_list *)reglist, reglist_len);
    if(ret != 0) {
        LOGE(OV2732_MODULE, " 2732 expo config error:%d \n", ret);
    }
    return ret; 
}

int32_t ov2732_config_reglist(uint32_t ir_mode, uint32_t addr, uint32_t data)
{
    uint32_t array_size = 0;
#if 0    
	if (CFG_MODE_IR_640x360_RAW == ir_mode) {
		array_size = ARRAY_SIZE(ov2732_1280x720_raw8);
		config_reglist(ov2732_1280x720_raw8, array_size, addr, data);
	} else if (CFG_MODE_IR_720P_RAW10 == ir_mode) {
		array_size = ARRAY_SIZE(ov2732_1280x720_raw10);
	config_reglist(ov2732_1280x720_raw8, array_size, addr, data);
	} else {
		printf("unkown ir mode\n");
		return -1;
	}
#else 
if (CFG_MODE_IR_1080P_RAW10 == ir_mode) {
		array_size = ARRAY_SIZE(ov2732_1920x1080_raw10);
		config_reglist(ov2732_1920x1080_raw10, array_size, addr, data);
	}

#endif
    return 0;
}


uint32_t ov2732_lookup_reg(uint32_t ir_mode, uint32_t addr)
{
    uint32_t array_size = 0;
    uint32_t ret = 0;
#if 0
	if (CFG_MODE_IR_640x360_RAW == ir_mode) {
		array_size = ARRAY_SIZE(ov2732_1280x720_raw8);
			ret = lookup_reg(ov2732_1280x720_raw8, array_size, addr);
	} else if (CFG_MODE_IR_720P_RAW10 == ir_mode) {
		array_size = ARRAY_SIZE(ov2732_1280x720_raw10);
		ret = lookup_reg(ov2732_1280x720_raw8, array_size, addr);
	}
#else
	if (CFG_MODE_IR_1080P_RAW10 == ir_mode) {
			array_size = ARRAY_SIZE(ov2732_1920x1080_raw10);
			ret = lookup_reg(ov2732_1920x1080_raw10, array_size, addr);
		}

#endif 
    return ret;
}
